package gov.nist.healthcare.testframework.communication.client;

import gov.nist.healthcare.core.message.HL7Message;
import gov.nist.healthcare.core.message.Name;
import gov.nist.healthcare.testframework.Actor;
import gov.nist.healthcare.testframework.Environment;
import gov.nist.healthcare.testframework.application.TestAgent;
import gov.nist.healthcare.testframework.communication.CommunicationUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.apache.log4j.Logger;

public abstract class ClientThread extends Thread {

    private static final Logger logger = Logger.getLogger(ClientThread.class.getName());
    protected Actor actor;
    protected HL7Message message;
    protected boolean secure;
    protected Socket socket;

    public void setActor(Actor actor) {
        this.actor = actor;
    }

    public void setMessage(HL7Message message) {
        this.message = message;
    }

    @Override
    public void run() {
        try {
            String endpoint = null;
            InetSocketAddress ip;
            if (secure) {
                ip = actor.getSecureInetSocketAddress();
                endpoint = actor.getSecurePath();
            } else {
                ip = actor.getOpenInetSocketAddress();
                endpoint = actor.getOpenPath();
            }
            if (ip != null) {
                socket = CommunicationUtils.createSocket(ip, secure);
                if (endpoint != null && !"".equals(endpoint)) {
                    CommunicationUtils.send(socket, encode(message, endpoint));
                } else {
                    CommunicationUtils.send(socket, encode(message));
                }
                Environment.getInstance().getEventModel().outgoingMessageTestFramework(
                        message.getSending(), message.getReceiving(), message);
                // Get the message
                HL7Message message = null;
                message = getMessage();
                if (message != null) {
                    Environment.getInstance().getEventModel().incomingMessageTestFramework(
                            message.getSending(), message.getReceiving(),
                            message);
                    // Pass the message to the test agent
                    Name receiving = message.getReceiving();
                    TestAgent receivingTestAgent = Environment.getInstance().getTestAgent(
                            receiving);
                    if (receivingTestAgent != null) {
                        receivingTestAgent.newMessage(message);
                        logger.info(String.format(
                                "Message received from %s and forwarded to %s",
                                socket.getRemoteSocketAddress(), receiving));
                        logger.info("\n" + message.getMessageAsString());
                    } else {
                        logger.debug(String.format(
                                "Can't get the receiving test agent: %s",
                                receiving));
                    }
                } else {
                    logger.debug("Can't get the message back from the responder");
                }
                socket.close();
            }
        } catch (IOException ioe) {
            logger.debug("Can't send message", ioe);
        }
    }

    /**
     * Encode the message before sending the data.
     * 
     * @param message
     * @return the encoded message based on the protocol
     */
    public abstract String encode(HL7Message message);

    /**
     * Encode the message before sending the data.
     * 
     * @param message
     * @param endpoint
     *        this is only used for SOAP
     * @return the encoded message based on the protocol
     */
    public abstract String encode(HL7Message message, String endpoint);

    /**
     * Process the data that are receiving by using the InputStream from the
     * Socket.
     * 
     * @return an HL7Message object
     */
    public abstract HL7Message getMessage();

}
